package chapter6;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

/**
 * @author caiya
 * @date 2020/3/30 20:10
 */
@Slf4j
public class ThreadPoolExecutorTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /**
         * ThreadPoolExecutor：线程池
         * 1、线程池解决的2个问题：
         *      1）执行大量异步任务时，线程池能提供较好的性能。避免 new 的线程创建开销。线程池中的线程是可复用的。
         *      2）线程池提供了资源限制及管理的手段，比如限制线程个数，动态新增线程等。
         * 2、线程池的几种状态：
         *      1）RUNNING: 接受新任务和处理阻塞队列里的任务
         *      2）SHUTDOWN: 拒绝新任务但是处理阻塞队列的任务
         *      3）STOP: 拒绝新任务同时抛弃阻塞队列里的任务，并且会中断正在处理的任务
         *      4）TIDYING: 所有任务都执行完（包括阻塞队列里的任务）后当前线程池中的活动线程数为0，将要调用terminated
         *      5）TERMINATED: 终止状态，terminated方法调用后的状态
         * 3、线程池配置参数如下：
         *      1）corePoolSize：核心线程数
         *      2）workQueue：用于保存等待执行任务的阻塞队列，可使用chapter5的阻塞队列
         *      3）maximumPoolSize：线程池的最大线程数
         *      4）ThreadFactory：创建线程的工厂
         *      5）RejectedExecutionHandler：决绝策略（当队列满，并且最大线程数已达到maximumPoolSize时处理新任务的策略）
         *              AbortPolicy：抛出异常
         *              CallerRunsPolicy：使用调用者所在线程来运行
         *              DiscardOldestPolicy：丢弃旧任务，接受新任务（内部使用pool获取并直接删除旧任务）
         *              DiscardPolicy：直接丢弃，不会抛出异常
         *      6）keepAliveTime：超出核心线程数的线程，当没有任务执行时的最大存活时间
         *      7）TimeUnit：最大存活时间的表示单位
         * 4、线程池的常见类型：(Executors.newXXX)
         *      1）newFixedThreadPool：创建一个核心线程=最大线程数=n，并且队列长度为Integer.MAX_VALUE，keepAliveTime为0的线程池。内部使用LinkedBlockingQueue。
         *      2）newSingleThreadPool：创建一个核心线程=最大线程数=1，并且队列长度为Integer.MAX_VALUE，keepAliveTime为0的线程池。内部使用LinkedBlockingQueue。
         *      3）newCachedThreadPool：创建一个核心线程为0，最大线程为Integer.MAX_VALUE，keepAliveTime为60s，队列使用SynchronousQueue的线程池。
         * 5、线程池执行原理：
         *      1）任务进来后，首先判断核心线程是否已满，没有的话新建线程执行
         *      2）如果核心线程数满了，那么判断阻塞队列是否已满，如果没有则将任务放入队列中等待执行
         *      3）如果队列已满，则判断线程数是否超出最大线程数，如果没有，则新建一个非核心线程运行任务
         *      4）如果超出最大线程数，则执行拒绝策略
         * 6、线程池的几种常用方法：
         *      1）void execute(Runnable command)：提交任务
         *      2）<T> Future<T> submit(Callable<T> command)：带返回的提交
         *      3）void shutdown()：关闭线程池，关闭后阻塞队列的任务仍在处理中。该方法会立即返回不会等待队列中的任务执行是否完毕
         *      4）List<Runnable> shutdownNow()：立即关闭，线程池不再接受新任务，并且队列中的任务会被丢弃，正在执行的任务会被中断。返回队列里面被丢弃的任务列表
         *      5）boolean awaitTermination(long timeout, TimeUnit unit)：阻塞当前线程，知道线程池变为TERMINATED终止或者超时时间到达才会返回
         */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 2, 0L,
                TimeUnit.SECONDS, new LinkedBlockingQueue<>(1), new ThreadPoolExecutor.CallerRunsPolicy());

        threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                log.debug(Thread.currentThread().getName() + "->" + ThreadLocalRandom.current().nextInt(100000));
            }
        });

        threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                log.debug(Thread.currentThread().getName() + "->" + ThreadLocalRandom.current().nextInt(100000));
            }
        });

        Future<String> stringFuture = threadPoolExecutor.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return Thread.currentThread().getName() + "->" + ThreadLocalRandom.current().nextInt(100000);
            }
        });
        while (true) {
            if (stringFuture.isDone()) {
                String s = stringFuture.get();
                log.debug("s: " + s);
                break;
            }
        }
        // 关闭线程池，关闭后任务队列还会继续执行结束
        threadPoolExecutor.shutdown();

        // 判断线程池是否执行完毕

        // 方法一：使用isTerminated
        while (true) {
            if (threadPoolExecutor.isTerminated()) {
                log.debug("所有子线程都执行完毕！退出...111");
                break;
            }
            Thread.sleep(1000);
        }

        // 方法二：使用awaitTermination
        boolean termination = threadPoolExecutor.awaitTermination(Integer.MAX_VALUE, TimeUnit.DAYS);
        if (termination) {
            log.debug("所有子线程都执行完毕！退出...222");
        }

        // 方法三：使用CountDownLatch
    }
}
